#pragma once
#include <vector>
#include <string>
#include <SceneGraph/node.h>
#include <SceneGraph/renderable.h>
#include <SceneGraph/animable.h>
#include <Dynamics/Integrator/integrator.h>
#include <Display/Renders/render.h>

namespace PhysLeo {

/**
 * Dynamic Body, extends Node, Renderable, Animable.
 * It can be one node of whole scene graph and be animable, renderable.
 */
template<typename T>
class DynamicBody :public Node, public Renderable, public Animable {
public:
    /**
     * explicit constructor
     * @param[in] integrator   Integrator method
     * @param[in] name   name of dynamic body
     */
	explicit DynamicBody( std::shared_ptr<Integrator<T>> integrator, std::string name="blank name");

    /**
    * delete default copy constructor
    */
    DynamicBody(const DynamicBody<T>&) = delete;

    /**
    * delete default move constructor
    */
    DynamicBody(DynamicBody<T>&&) = delete;

    /**
    * delete default copy assignment function
    */
    DynamicBody<T>& operator=(const DynamicBody<T>&) = delete;

    /**
    * delete default move assignment function
    */
    DynamicBody<T>& operator=(DynamicBody<T>&&) = delete;

    /**
     * default virtual destructor
     */
    virtual ~DynamicBody() = default;

    /**
     * add render to this dynamic body
     * @param[in] render   render to be added
     */
	void addRender(std::shared_ptr<Render<T>> render);

    /**
     * get the simulation data
     * @return shared_ptr to simulation data of this dynamic body
     */
	std::shared_ptr<SimData<T>> getSimData() { return sim_data_; }

    /**
     * get name string
     * @return body name string
     */
    std::string name() const { return body_name_; }

    /**
    * init openGL resource
    */
    void initGlResource() override;

    /**
    * draw entity
    * @param[in] camera  a camera object which implements camera interface
    */
    void draw(CameraInterface &camera) override;

    /**
    * init simulation resource
    */
    void initSimulationResource() override;

    /**
    * animation steps forward dt second
    * @param[in] dt  time for one frame
    */
    void run(float dt) override;

    /**
    * transform simulation data to render data
    */
    void transformDrawData() override;

    /**
    * update render data to GL buffer
    */
    void updateDrawData() override;

protected:
    /**
     * body name string
     */
	std::string body_name_;

    /**
     * simulation data
     */
	std::shared_ptr<SimData<T>> sim_data_;

    /**
     * integrator method, animation algorithm
     */
	std::shared_ptr<Integrator<T>> integrator_;

    /**
     * vector container of renders
     */
	std::vector<std::shared_ptr<Render<T>>>  renders_;
};

}